#!/bin/bash

#
# (c) 2009 Mandriva, http://www.mandriva.com/
# (c) 2009 Jean-Dominique Frattini, jd.frattini@free.fr
#
# $Id: $
#
# This file is part of Test-Cloud, 
# http://http://wiki.mandriva.com/en/Test-Cloud
#
# Test-Cloud is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Test-Cloud is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Pulse 2; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.

# usage: testcloud-testrunner.sh

#
# This file is the main part of the testing process on client side.
#
# 1: launch urpmi for the retrieved package and prints out the std and err log 
#     in log/<$pkg_name>/install.log and ~/uninstall.log
# 2: test all binaries in the package
# 3: test binary-man coherencies
# 4: test library dependencies
# 5: test srpm build [WORK IN PROGRESS]
# 6: then launch urpme in order to uninstall
# 7: compress the log directory into a tar.gz file
# 8: send the archive back to the server
#

# bad but easiest way to do so
prog_name="testcloud-testrunner"

install_dir="/usr"
log_dir="/var/log/${prog_name}"
root_uid=0
kernel_v=`uname -r`
no_package=0

# exit values
exit_ok=0
exit_file_missing=1
exit_upload_error=2
exit_bad_package=3
exit_server_log_error=4
exit_bad_package_name=5
exit_bad_dist=6
exit_network_unreachable=7

# the command to use for package installation:
urpmi_cmd="urpmi --auto --verbose --clean --verify-rpm --quiet "


# Ensure needed files are present:
testcloud_check_install()
{
   echo "Checking installation"

   for i in annotate-output.sh timered-sh.sh; do
      if ! test -f ${install_dir}/bin/${i}
      then
         echo " ERROR: ${install_dir}/bin/${i} is missing" >&2
     testcloud_halt
         exit ${exit_file_missing}
      fi
   done

   for i in config key; do
      if ! test -f /etc/${prog_name}/${i}
      then
         echo " ERROR: /etc/${prog_name}/${i} is missing" >&2
     testcloud_halt
         exit ${exit_file_missing}
      fi
   done

   echo " Done."
}


# Reading config:
testcloud_read_conf()
{
   echo "Reading conf files."

   while read line; do
      iserver=${line}
      read irequest
      read oserver
      read orequest
      read time_limit
   done < "/etc/${prog_name}/config"

   echo " Done."
}


# Take back version and arch:
testcloud_get_dist_info()
{
   echo "Getting distribution information"

   if [ -f /etc/mandriva-release ]; then # Mandriva
      version=`awk -F "," '{ split ($4,tab,"="); print tab[2]; }' /etc/product.id`
      arch=`awk -F "," '{ split ($7,tab,"="); print tab[2]; }' /etc/product.id`
   else
      echo " ERROR: Your distribution is not yet supported."
      testcloud_halt
      exit ${exit_bad_dist}
   fi

   echo " Done."
}


testcloud_update_media()
{
   echo "Updating media, this could take a moment."

   urpmi.update -a
#   urpmi --auto-select this will update some packages, is this what we want ?
}


# Get the package name:
testcloud_test_get_pkg_name()
{
   echo "Retrieving package to test"

   start_time=`date +%s`
   pkg_name=`curl -s "${iserver}${irequest}&version=${version}&arch=${arch}&kernel=${kernel_v}"`
   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))

   if [ $? -ne 0 ]; then
      echo " Cannot connect to ${iserver}"
      testcloud_halt
      exit ${exit_network_unreachable}
   fi

   echo " Received: \"${pkg_name}\""

   # Stop if the package name is ${no_package}:
   if [ "${pkg_name}" = ${no_package} ]; then
      echo " No package available for test !"
      testcloud_halt
      exit ${exit_ok}
   fi

   # Testing the package name:
   if `echo ${pkg_name} | grep -Eq '^[a-zA-Z0-9_\.-]+$'`
   then
      match=`echo ${pkg_name} | grep [\.]rpm$`

      if [ "${match}" = "" ]; then
     echo " Package name checked"
      else
         echo " ERROR: Package name should not contain the extension \".rpm\""
     testcloud_send_logs_no_finished
         testcloud_halt
         exit ${exit_bad_package_name}
      fi
   else
      echo " ERROR: package name check failed"
      testcloud_send_logs_no_finished
      testcloud_halt
      exit ${exit_bad_package_name}
   fi

   echo " download time: ${diff_time} seconds."
   mkdir ${log_dir}/${pkg_name} 2> /dev/null
}


# Test can start now:
testcloud_test_start_time()
{
   begin_time=`date`
   echo "Start time: ${begin_time}"
   cwd=`pwd`
   du_k=`du -ks / 2> /dev/null`
   echo "Disque usage at start: ${du_k}"
   cd ${cwd}
}


# urpmi error code
testcloud_test_urpmi_error_code()
{
   case "$1" in 
      1)
     echo " Command line inconsistency."
     ;;
      2)
     echo " Problem registering local packages."
     ;;
      3)
     echo " Source packages not retrievable."
     ;;
      4)
     echo " Medium is not selected."
     ;;
      5)
     echo " Medium already exists."
     ;;
      6)
     echo " Unable to save configuration."
     ;;
      7)
     echo " urpmi database locked."
     ;;
      8)
     echo " Unable to read or create bug report."
     ;;
      9)
     echo " Unable to open rpmdb."
     ;;
      10)
     echo " Some files are missing for installation."
     ;;
      11)
     echo " Some transactions failed but not all."
     ;;
      12)
     echo " All transactions failed."
     ;;
      13)
     echo " Some files are missing and some transactions failed but not all."
     ;;
      14)
     echo " Some files are missing and all transactions failed."
     ;;
      15)
     echo " No package installed (when using --expect-install)"
     ;;
      16)
     echo " Bad signature"
     ;;
      17)
     echo " Some packages couldn't be installed or upgraded"
     ;;
      *)
     echo " Error code not listed by urpmi..."
   esac
}


# Installation:
testcloud_test_install()
{
   echo "Installing \"${pkg_name}\""

   start_time=`date +%s`
   notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=install"`
   annotate-output.sh ${urpmi_cmd} ${pkg_name} > ${log_dir}/${pkg_name}/install.log

   if [ $? -ne 0 ]; then
      echo " ERROR: urpmi failed with code: $?"
      testcloud_test_urpmi_error_code $?
      testcloud_compress
      testcloud_send_logs
      testcloud_halt
      exit ${exit_bad_package}
   fi

   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))
   echo " installation time: ${diff_time} seconds."
}


# Retrieving file lists
testcloud_test_get_file_list()
{
   echo "Getting file/executable/library list"
   notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=file_list"`
   
   annotate-output.sh rpm -qvl ${pkg_name} > ${log_dir}/${pkg_name}/files.log
   touch ${log_dir}/${pkg_name}/executables.log
   touch ${log_dir}/${pkg_name}/libraries.log
   s=`rpm -qvl ${pkg_name} | grep '^[^d]' | awk ' { print $9 } '`

   for i in $s; do
      if [ -x $i ]; then
     # executable or library ?
     b=`echo $i | grep '[\.]so'`

     if [ "$b" = "" ]; then
        echo $i >> ${log_dir}/${pkg_name}/executables.log
     else
        echo $i >> ${log_dir}/${pkg_name}/libraries.log
     fi
      fi
   done
}


# Testing of binaries:
testcloud_test_bin()
{
   a=0

   while read line
   do a=$((${a}+1));
      bn=`echo "${line}" | awk -F "/" '{ print $NF }'`
      
      # first test binary dependencies
      echo "Testing binary dependencies of ${line}"
      notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=bin_test"`
      annotate-output.sh ldd -r ${line} > ${log_dir}/${pkg_name}/${bn}.ldd.log
      
      # then test the binary
      echo "Testing binary ${line}"
      start_time=`date +%s`
      annotate-output.sh file ${line} > ${log_dir}/${pkg_name}/${bn}.log
      end_time=`date +%s`
      diff_time==$((${end_time}-${start_time}))
      echo " execution time of \"${bn}\": ${diff_time} seconds." >> ${log_dir}/${pkg_name}/${bn}.log
      echo "" >> ${log_dir}/${pkg_name}/${bn}.log

      # this ensures the binary will give hand back after time_limit seconds
      timered-sh.sh ${time_limit} "annotate-output.sh ${line}" >> ${log_dir}/${pkg_name}/${bn}.log
      # same but with --help and -h options:
      timered-sh.sh ${time_limit} "annotate-output.sh ${line} --help" >> ${log_dir}/${pkg_name}/${bn}.log
      timered-sh.sh ${time_limit} "annotate-output.sh ${line} -h" >> ${log_dir}/${pkg_name}/${bn}.log
      
      # binary/man coherency
      echo "Testing binary-man coherency of ${line}"
      cat ${log_dir}/${pkg_name}/files.log | grep man | grep ${bn} > ${log_dir}/${pkg_name}/${bn}.man.log
   done < "${log_dir}/${pkg_name}/executables.log"
}


# Testing library dependencies:
testcloud_test_lib_deps()
{
   a=0

   while read line
   do a=$((${a}+1));
      so=`echo "${line}" | awk -F "/" '{ print $NF }'`
      # testing library
      echo "Testing library ${line}"
      notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=lib_test"`
      annotate-output.sh file -L ${line} > ${log_dir}/${pkg_name}/${so}.log
      
      # test library dependencies
      echo "Testing library dependencies of ${line}"
      annotate-output.sh ldd -r ${line} > ${log_dir}/${pkg_name}/${so}.ldd.log
   done < "${log_dir}/${pkg_name}/libraries.log"
}


# Uninstallation:
testcloud_test_uninstall()
{
   echo "Uninstalling "
   start_time=`date +%s`
   notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=uninstall"`
   annotate-output.sh urpme --auto ${pkg_name} > ${log_dir}/${pkg_name}/uninstall.log

   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))
   echo " uninstallation time: ${diff_time} seconds."
}


# Get the time when test has finished
testcloud_test_stop_time()
{
   stop_time=`date`
   echo "Stop time: ${stop_time}"
   cwd=`pwd`
   du_k=`du -ks / 2> /dev/null`
   echo "Disque usage at end: ${du_k}"
   cd ${cwd}
}


# Build try of the package:
testcloud_test_build_pkg()
{
   echo "Downloading source package."
   notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=build"`
   annotate-output.sh urpmi --install-src ${pkg_name} > ${log_dir}/${pkg_name}/build.log
   
   ret=$?

   if [ $ret -ne 0 ]; then
      echo "urpmi ended with error code: $ret"
      testcloud_test_urpmi_error_code $ret
   else
      # TODO: test the package build [CURRENT]
      cwd=`pwd`
      cd ~/rpmbuild/SPECS
      #annotate-output.sh rpmbuild -ba *.spec >> ${log_dir}/${pkg_name}/build.log
      cd ${cwd}
   fi
}


# Compression:
testcloud_compress()
{
   notify=`curl -s "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}&package=${pkg_name}&stage=compressing"`
   echo "Compressing \"${pkg_name}\" into tar.gz"

   start_time=`date +%s`

   if [ -f ${log_dir}/testcloud_guest_client.log ]; then
      mv ${log_dir}/testcloud_guest_client.log ${log_dir}/${pkg_name}/
   fi

   cwd=`pwd`
   cd ${log_dir}
   tar -czf ${pkg_name}.tar.gz ${pkg_name}/
   cd $cwd

   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))
   echo " compression time: ${diff_time} seconds."
}


# Sending the test logs to the server:
testcloud_send_logs_no_finished()
{
   echo "Sending logs to the server"
   
   tar -czf ${log_dir}/testcloud_guest_client.log.tar.gz ${log_dir}/testcloud_guest_client.log

   read key < /etc/${prog_name}/key
   res=`curl -s -F "data=@${log_dir}/testcloud_guest_client.log.tar.gz" -F "pkgname=none" -F "logfile=testcloud_guest_client.log.tar.gz" -F "opLogUpload=Upload" -F "key=${key}" "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}"`

   result=`echo "$res" | awk -F " " '{ print $1 }'`
   result_code=`echo "$res" | awk -F " " '{ print $2 }'`
   log=`echo "$res" | awk -F " " '{ print $3 }'`
   log_url=`echo "$res" | awk -F " " '{ print $4 }'`

   if [ "${result}" != "result" ]; then
      echo "ERROR: Logs upload was not accepted by the server"
      echo "Server error is: ${result}"
      testcloud_halt
      exit ${exit_server_log_error}
   fi

   if [ "${result_code}" = 200 ]; then
      echo "Archive has succesfully been uploaded."
      echo ""
      echo "You can access the logs at the url: ${log_url}"
   else
      echo "ERROR: Transfert failed with error: ${result_code}" >&2
      testcloud_halt
      exit ${exit_upload_error}
   fi
}


# Sending the test logs to the server:
testcloud_send_logs()
{
   echo "Sending logs to the server"

   read key < /etc/${prog_name}/key
   res=`curl -s -F "data=@${log_dir}/${pkg_name}.tar.gz" -F "pkgname=${pkg_name}" -F "logfile=${pkg_name}.tar.gz" -F "opLogUpload=Upload" -F "key=${key}" "${oserver}${orequest}&version=${version}&arch=${arch}&kernel=${kernel_v}"`

   result=`echo "$res" | awk -F " " '{ print $1 }'`
   result_code=`echo "$res" | awk -F " " '{ print $2 }'`
   log=`echo "$res" | awk -F " " '{ print $3 }'`
   log_url=`echo "$res" | awk -F " " '{ print $4 }'`

   if [ "${result}" != "result" ]; then
      echo "ERROR: Logs upload was not accepted by the server"
      echo "Server error is: ${result}"
      testcloud_halt
      exit ${exit_server_log_error}
   fi

   if [ "${result_code}" = 200 ]; then
      echo "Archive has succesfully been uploaded."
      echo ""
      echo "You can access the logs at the url: ${log_url}"
   else
      echo "ERROR: Transfert failed with error: ${result_code}" >&2
      testcloud_halt
      exit ${exit_upload_error}
   fi
}


# Halting:
testcloud_halt()
{
   # Removing temporary data:
   echo "Removing temporary data"
   rm -fr ${log_dir}/${pkg_name}
   rm -f ${log_dir}/${pkg_name}.tar.gz

   echo "Ending."
   testcloud-vm-halt.sh
}


# Test file list before:
# give before_install or after_install or after_uninstall as argument
# Notes: maybe should I forget /dev/, /sys and /proc here ?
testcloud_test_fl()
{
   echo "Inspecting files and directories ${1}"

   start_time=`date +%s`

   echo " getting all files"
   find / > /tmp/all_file_list.${1}
   touch /tmp/file_list.${1}
   touch /tmp/dir_list.${1}
   touch /tmp/file_size.${1}

   echo " dispatching files and directories"
   for f in `cat /tmp/all_file_list.${1}`; do
      if [ -f ${f} ]; then
     echo ${f} >> /tmp/file_list.${1}
     du -b ${f} >> /tmp/file_size.${1}
      elif [ -d ${f} ]; then
     echo ${f} >> /tmp/dir_list.${1}
      fi
   done

   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))
   echo " inspection (${1}) time: ${diff_time} seconds."
   echo " Done."
}


# Make the diff:
testcloud_test_fl_diff()
{
   echo "Making diff on files and directories"
   start_time=`date +%s`

   # diffs on files
   echo " making diff on files"
   diff /tmp/file_list.before_install /tmp/file_list.after_install > ${log_dir}/${pkg_name}/prein-postin-diff.filelist.log
   diff /tmp/file_list.after_install /tmp/file_list.after_uninstall > ${log_dir}/${pkg_name}/postin-postun-diff.filelist.log
   diff /tmp/file_list.before_install /tmp/file_list.after_uninstall > ${log_dir}/${pkg_name}/prein-postun-diff.filelist.log

   # diffs on file sizes
   echo " making diff on file sizes"
   diff /tmp/file_size.before_install /tmp/file_size.after_install > ${log_dir}/${pkg_name}/prein-postin-diff.filesize.log
   diff /tmp/file_size.after_install /tmp/file_size.after_uninstall > ${log_dir}/${pkg_name}/postin-postun-diff.filesize.log
   diff /tmp/file_size.before_install /tmp/file_size.after_uninstall > ${log_dir}/${pkg_name}/prein-postun-diff.filesize.log

   # diffs on directories
   echo " making diff on directories"
   diff /tmp/dir_list.before_install /tmp/dir_list.after_install > ${log_dir}/${pkg_name}/prein-postin-diff.dirlist.log
   diff /tmp/dir_list.after_install /tmp/dir_list.after_uninstall > ${log_dir}/${pkg_name}/postin-postun-diff.dirlist.log
   diff /tmp/dir_list.before_install /tmp/dir_list.after_uninstall > ${log_dir}/${pkg_name}/prein-postun-diff.dirlist.log

   rm -f /tmp/file_list.before_install
   rm -f /tmp/file_list.after_install
   rm -f /tmp/file_list.after_uninstall
   rm -f /tmp/file_size.before_install
   rm -f /tmp/file_size.after_install
   rm -f /tmp/file_size.after_uninstall
   rm -f /tmp/dir_list.before_install
   rm -f /tmp/dir_list.after_install
   rm -f /tmp/dir_list.after_uninstall

   end_time=`date +%s`
   diff_time==$((${end_time}-${start_time}))
   echo " diff time: ${diff_time} seconds."
   echo " Done."
}


# Test if links (files and web pages) refered into man pages do not exist
testcloud_test_man()
{
   echo "Inspecting man pages"

   mp_list=`cat ${log_dir}/${pkg_name}/files.log | grep '/usr/share/man' | awk -F "/" ' { print $0 } '`

   for mp in ${mp_list}; do
      echo " inspecting ${mp}"
      url_list=`lzmadec ${mp} | grep ' /'`

      for url in ${url_list}; do
     if ! [ -f ${url} ]; then
        echo "Reference: \"${url}\" missing." >> ${log_dir}/${pkg_name}/man_${mp}.log
     fi
      done
      
      url_list=`lzmadec ${mp} | grep 'http://'`

      for url in ${url_list}; do
     wget ${url}

     if [ ${?} -ne 0 ]; then
        echo "Reference: \"${url}\" missing." >> ${log_dir}/${pkg_name}/man_${mp}.log
     fi
      done
   done

   echo " Done."
}

# running
testcloud_check_install
testcloud_read_conf
testcloud_get_dist_info
#testcloud_update_media
testcloud_test_get_pkg_name
testcloud_test_start_time
#testcloud_test_fl "before_install"
testcloud_test_install
#testcloud_test_fl "after_install"
testcloud_test_get_file_list
testcloud_test_bin
testcloud_test_lib_deps
testcloud_test_man
testcloud_test_uninstall
#testcloud_test_fl "after_uninstall"
#testcloud_test_fl_diff
#testcloud_test_build_pkg # work in progress
testcloud_test_stop_time
testcloud_compress
testcloud_send_logs
testcloud_halt

exit ${exit_ok} # should never arrive here...
